#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Ink.h"

#include "Class.h"
#include "String.h"
#include "Port.h"
#include "WindowSystem.h"
#include "Math.h"
#include "OrdColl.h"
#include "WindowPort.h"
#include "WindowColorMap.h"
#include "CType.h"
#include "Env.h"
#include "ET_stdio.h"

const int
	MaxWord1= cMaxRGB+1,
	cellsz= 8,
	levels= cellsz*cellsz,
	cMaxPatterns= 22;

InkManager *gInkManager;

Ink *gInkXor, *gInkNone, *gInkWhite, *gInkBlack, *gHighlightColor,
	*gInkRed, *gInkGreen, *gInkBlue,
	*gInkYellow, *gInkMagenta, *gInkCyan,
	*gInkViolet, *gInkOrange;
      
// obsolete
Ink *ePatXor, *ePatNone, *ePatWhite, *ePatBlack;


static u_short PatBits[cMaxPatterns][8]= {
	{ 0x8000,0x0000,0x0800,0x0000,0x8000,0x0000,0x0800,0x0000 },
	{ 0x8800,0x0000,0x2200,0x0000,0x8800,0x0000,0x2200,0x0000 },
	{ 0xaa00,0x0000,0xaa00,0x0000,0xaa00,0x0000,0xaa00,0x0000 },
	{ 0xaa00,0x5500,0xaa00,0x5500,0xaa00,0x5500,0xaa00,0x5500 },
	{ 0x5500,0xff00,0x5500,0xff00,0x5500,0xff00,0x5500,0xff00 },
	{ 0x7700,0xff00,0xdd00,0xff00,0x7700,0xff00,0xdd00,0xff00 },
	{ 0x7f00,0xff00,0xf700,0xff00,0x7f00,0xff00,0xf700,0xff00 },
	{ 0x9900,0xCC00,0x6600,0x3300,0x9900,0xCC00,0x6600,0x3300 },
	{ 0xff00,0x0000,0xff00,0x0000,0xff00,0x0000,0xff00,0x0000 },
	{ 0x5500,0x5500,0x5500,0x5500,0x5500,0x5500,0x5500,0x5500 },
	{ 0x4400,0x8800,0x1100,0x2200,0x4400,0x8800,0x1100,0x2200 },
	{ 0xbb00,0x7700,0xee00,0xdd00,0xbb00,0x7700,0xee00,0xdd00 },
	{ 0xff00,0x0000,0x0000,0x0000,0xff00,0x0000,0x0000,0x0000 },
	{ 0x0000,0xff00,0xff00,0xff00,0x0000,0xff00,0xff00,0xff00 },
	{ 0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000 },
	{ 0xfe00,0xfd00,0xfb00,0xf700,0xef00,0xdf00,0xbf00,0x7f00 },
	{ 0x5500,0x8800,0x5500,0x2200,0x5500,0x8800,0x5500,0x2200 },
	{ 0xaa00,0x7700,0xaa00,0xdd00,0xaa00,0x7700,0xaa00,0xdd00 },
	{ 0x8800,0x8800,0x8800,0x8800,0x8800,0x8800,0x8800,0x8800 },
	{ 0xee00,0xee00,0xee00,0xee00,0xee00,0xee00,0xee00,0xee00 },
	{ 0xaa00,0x5500,0xaa00,0x5500,0xaa00,0x5500,0xaa00,0x5500 },
	{ 0x8000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 }
};

static Bitmap *patterns[cMaxPatterns+1];
static Bitmap **patbms;
static byte *ditherm;

Ink *ePatGrey12, *ePatGrey25, *ePatGrey40, *ePatGrey50;
Ink *ePatGrey60, *ePatGrey75, *ePatGrey87;
Ink *ePat00, *ePat01, *ePat02, *ePat03, *ePat04, *ePat05;
Ink *ePat06, *ePat07, *ePat08, *ePat09, *ePat10, *ePat11;
Ink *ePat12, *ePat13, *ePat14, *ePat15;

//---- NamedRGB ----------------------------------------------------------------

class NamedRGB : public Object {
public:
	RGB rgb;
	char *name;
public:
	MetaDef(NamedRGB);
	NamedRGB(const RGB &c, const char *nm) : rgb(c)
		{ name= strsave(nm); }
	~NamedRGB()
		{ SafeDelete(name); }
};

NewMetaImpl(NamedRGB,Object, (T(rgb), TP(name)));

//---- Ink ---------------------------------------------------------------------

NewMetaImpl(Ink,Object, (T(id)));

Ink::Ink()
{
	id= -1;
}
		
Ink::Ink(long i)
{
	id= i;
}

void Ink::SetId(long i)
{
	id= i;
}

void Ink::SetInk(Port *p)
{
	p->DevSetOther((int)id);
}

bool Ink::IsEqual(Object *op)
{
	return Object::IsEqual(op);
}

OStream& Ink::PrintOn(OStream &s)
{
	Object::PrintOn(s);
	return s << id SP;
}

IStream& Ink::ReadFrom(IStream &s)
{
	Object::ReadFrom(s);
	return s >> id;
}

Object *Ink::ReadAndMap(IStream &is)
{
	if (IsA() != Meta(Ink))
		return Object::ReadAndMap(is);
	int id;
	Object::ReadFrom(is);

	is >> id;
	switch (id) {
	case -1:
		return gInkXor;
	case 0:
		return gInkNone;
	case 1:
		return gInkWhite;
	case 2:
		return gInkBlack;
	default:
		return new Ink(id);
	}
}

//---- InkManager --------------------------------------------------------------

NewMetaImpl(InkManager, Object, (TP(sharedinks), TP(namedRGBMap)));

InkManager::InkManager()
{
	sharedinks= new OrdCollection;

	ePatXor= gInkXor= new Ink(-1);
	sharedinks->Add(gInkXor);
	ePatNone= gInkNone= new Ink((int)0);
	sharedinks->Add(gInkNone);
	ePatWhite= gInkWhite= new Ink(1);
	sharedinks->Add(gInkWhite);
	ePatBlack= gInkBlack= new Ink(2);
	sharedinks->Add(gInkBlack);
	
#if 1
	gInkRed= new RGBColor(255, 0, 0);
	sharedinks->Add(gInkRed);
	gInkGreen= new RGBColor(0, 255, 0);
	sharedinks->Add(gInkGreen);
	gInkBlue= new RGBColor(0, 0, 255);
	sharedinks->Add(gInkBlue);
	gInkMagenta= new RGBColor(255, 0, 255);
	sharedinks->Add(gInkMagenta);
	gInkCyan= new RGBColor(0, 255, 255);
	sharedinks->Add(gInkCyan);
	gInkYellow= new RGBColor(255, 255, 0);
	sharedinks->Add(gInkYellow);
	gInkViolet= new RGBColor(238, 130, 238);
	sharedinks->Add(gInkViolet);
	gInkOrange= new RGBColor(255, 165, 0);
	sharedinks->Add(gInkOrange);
#else
	gInkRed=   gInkBlack;
	gInkGreen= gInkBlack;
	gInkBlue=  gInkBlack;
	gInkMagenta= gInkBlack;
	gInkCyan=   gInkBlack;
	gInkYellow= gInkBlack;
	gInkViolet= gInkBlack;
	gInkOrange= gInkBlack;
#endif

	for (int i= 0; i < cMaxPatterns; i++)
		patterns[i]= new Bitmap(gPoint8, PatBits[i]);
		
	ePatGrey12= patterns[0];
	ePatGrey25= patterns[1];
	ePatGrey40= patterns[2];
	ePatGrey50= patterns[3];
	ePatGrey60= patterns[4];
	ePatGrey75= patterns[5];
	ePatGrey87= patterns[6];
	ePat00= patterns[7];
	ePat01= patterns[8];
	ePat02= patterns[9];
	ePat03= patterns[10];
	ePat04= patterns[11];
	ePat05= patterns[12];
	ePat06= patterns[13];
	ePat07= patterns[14];
	ePat08= patterns[15];
	ePat09= patterns[16];
	ePat10= patterns[17];
	ePat11= patterns[18];
	ePat12= patterns[19];
	ePat13= patterns[20];
	ePat14= patterns[21];
	ePat15= patterns[21];
	
	namedRGBMap= 0;
}

InkManager::~InkManager()
{
	if (namedRGBMap) {
		namedRGBMap->FreeAll();
		delete namedRGBMap;
		namedRGBMap= 0;
	}

	gInkXor= gInkNone= gInkWhite= gInkBlack= 0;
	ePatXor= ePatNone= ePatWhite= ePatBlack= 0;

	for (int i= 0; i < cMaxPatterns; i++)
		SafeDelete(patterns[i]);
	if (patbms) {
		for (i= 0; i < levels; i++)
			SafeDelete(patbms[i]);
		SafeDelete(patbms);
	}
	SafeDelete(ditherm);

	if (sharedinks) {
		sharedinks->FreeAll();
		SafeDelete(sharedinks);
	}
}

bool InkManager::Init()
{
	return FALSE;
}

void InkManager::ReadRgbNames()
{
	FILE *ifp;

	namedRGBMap= new OrdCollection;
	ifp= fopen("/home/europa/packages/openwin3/lib/rgb.txt", "r");
	if (ifp == 0)
		ifp= fopen("/usr/lib/X11/rgb.txt", "r");
	if (ifp) {
		char line[512], name[512];
		RGB c;
		while (fgets(line, sizeof(line), ifp)) {
			int r, g, b;
			if (sscanf(line, "%d %d %d %[^\n]\n", &r, &g, &b, name) != 4)
				continue;
			if (r < 0 || r > 0xFF || g < 0 || g > 0xFF || b < 0 || b > 0xFF)
				continue;
			c.red= r; c.green= g; c.blue= b; c.alpha= 255;
			namedRGBMap->Add(new NamedRGB(c, name));
		}
		fclose(ifp);
		c.red= c.green= c.blue= c.alpha= 0;
		namedRGBMap->Add(new NamedRGB(c, "none"));
	}
}

char *InkManager::FindName(const RGB &c, bool exact)
{
	if (namedRGBMap == 0)
		ReadRgbNames();
	Iter next(namedRGBMap);
	register NamedRGB *rgb;

	if (exact) {
		while (rgb= (NamedRGB*) next())
			if (rgb->rgb == c)
				return rgb->name;
	} else {
		register NamedRGB *best;
		RGB d;
		int dist, mindist= 256*256*3;

		while (rgb= (NamedRGB*) next()) {
			d= rgb->rgb - c;
			dist= (d.red*d.red) + (d.green*d.green) + (d.blue*d.blue);
			if (dist < mindist) {
				best= rgb;
				mindist= dist;
			}
		}
		return best->name;
	}
	return 0;
}

RGB *InkManager::FindRGB(const char *name)
{
	if (namedRGBMap == 0)
		ReadRgbNames();
	Iter next(namedRGBMap);
	register NamedRGB *c;

	while (c= (NamedRGB*) next())
		if (StrCmp(c->name, name, -1, gStdEncoding->UpperCaseMap()) == 0)
			return &c->rgb;
	return 0;
}

bool InkManager::ParseColor(RGB &rgb, const char *c)
{
	rgb.alpha= 255;
	if (*c == '#') {
		c++;
		int l= strlen(c)/3;
		int r, g, b;
		char fmt[30];
		sprintf(fmt, "%%%dx%%%dx%%%dx", l, l, l);
		if (sscanf((char*) c, fmt, &r, &g, &b) != 3) {
			fprintf(stderr, "ParseColor: can't parse %s\n", c[-1]);
			return FALSE;
		}
		switch (l) {
		case 1:
			r*= 17;
			g*= 17;
			b*= 17;
			break;
		case 2:
			break;
		case 3:
			r/= 17;
			g/= 17;
			b/= 17;
			break;
		case 4:
			r/= 257;
			g/= 257;
			b/= 257;
			break;
		}
		rgb.red= r;
		rgb.green= g;
		rgb.blue= b;
		return TRUE;
	}
	if (StrCmp(c, "none", -1, gStdEncoding->UpperCaseMap()) == 0) {
		rgb.alpha= 0;
		return TRUE;
	}

	RGB *cc= FindRGB(c);
	if (cc) {
		rgb= *cc;
		return TRUE;
	}

	rgb.red= rgb.green= rgb.blue= cMinRGB;
	return FALSE;
}

RGBColor *new_RGBColor(const char *name, short prec)
{
	RGBColor *c= new RGBColor(*gInkManager->FindRGB(name), prec);
	gInkManager->sharedinks->Add(c);
	return c;
}

RGBColor *new_RGBColor(short r, short g, short b, short prec)
{
	RGBColor *c= new RGBColor(r, g, b, prec);
	gInkManager->sharedinks->Add(c);
	return c;
}

RGBColor *new_Grey(float graylevel, short prec)
{
	RGBColor *c= new RGBColor(graylevel, prec);
	gInkManager->sharedinks->Add(c);
	return c;
}

Ink *new_Ink(const char *name, Ink *dflt)
{
	return (Ink*) Env::GetValue(name, (Object*)dflt);
}

//---- RGB ---------------------------------------------------------------------

SimpleMetaImpl(RGB)
{
	RGB *c= (RGB*)addr;
	len= len;
	if (! isptr)
		sprintf(buf,"r:%d g:%d b:%d a:%d", c->red, c->green, c->blue, c->alpha);
}

RGB::RGB(const RGB &c)
{
	red= c.red;
	green= c.green;
	blue= c.blue;
	alpha= c.alpha;
}

RGB::RGB(const HSVColor &hsv)
{
	register int i, f, h;
	int p, q, t, hue= hsv.hue, saturation= hsv.saturation, value= hsv.value;

	if (hue > 359)
		hue-= 360;
	else if (hue < 0)
		hue+= 360;

	if (saturation == 0)
		red= green= blue= value;    // achromatic color: there is no hue
	else {
		h= hue * MaxWord1 / 60;
		i= h / MaxWord1 * MaxWord1;
		f= h - i;
		p= value * (MaxWord1 - saturation) / MaxWord1;
		q= value * (MaxWord1 - (saturation*f)/MaxWord1) / MaxWord1;
		t= value * (MaxWord1 - (saturation * (MaxWord1 - f))/MaxWord1) / MaxWord1;
		switch (i / MaxWord1) {
		case 0:
			red= value;     green= t;       blue= p;
			break;
		case 1:
			red= q;         green= value;   blue= p;
			break;
		case 2:
			red= p;         green= value;   blue= t;
			break;
		case 3:
			red= p;         green= q;       blue= value;
			break;
		case 4:
			red= t;         green= p;       blue= value;
			break;
		case 5:
			red= value;     green= p;       blue= q;
			break;
		}
	}
	alpha= hsv.alpha;
}

RGB::RGB(float graylevel)
{
	red= green= blue= (short) (graylevel * cMaxRGB);
	alpha= cMaxRGB;
}

void RGB::SetRGB(short r, short g, short b, short a)
{
	red= Math::Range((short)0, (short)cMaxRGB, r);
	green= Math::Range((short)0, (short)cMaxRGB, g);
	blue= Math::Range((short)0, (short)cMaxRGB, b);
	alpha= Math::Range((short)0, (short)cMaxRGB, a);
}

short RGB::AsGreyLevel()
{
	if (red == green && green == blue)
		return red;
	register int l= (int) (0.299 * red + 0.587 * green + 0.114 * blue + 0.5);
	if (l > cMaxRGB)
		return cMaxRGB;
	return l;
}

OStream& RGB::PrintOn(OStream &os)
{
	return os << red SP << green SP << blue SP << alpha NL;
}

IStream& RGB::ReadFrom(IStream &is)
{
	//if (Meta(RGBColor)->LoadVersion() < 3.0
	//               || Meta(Bitmap)->LoadVersion() < 3.0) {
	//        return is >> red >> green >> blue;
	//}
	return is >> red >> green >> blue >> alpha;
}

//---- HSVColor ----------------------------------------------------------------

SimpleMetaImpl(HSVColor)
{
	HSVColor *c= (HSVColor*)addr;
	len= len;
	if (! isptr)
		sprintf(buf, "h:%d s:%d v:%d a:%d", c->hue, c->saturation,
														c->value, c->alpha);
}

HSVColor::HSVColor(const HSVColor &c)
{
	hue= c.hue;
	saturation= c.saturation;
	value= c.value;
	alpha= c.alpha;
}

HSVColor::HSVColor(const RGBColor &rc)
{
	RGB rgb(rc.rgb);
	int cmax= Math::Max(Math::Max(rgb.red, rgb.green), rgb.blue);
	int cmin= Math::Min(Math::Min(rgb.red, rgb.green), rgb.blue);

	value= cmax;
	if (cmax)
		saturation= (cmax - cmin) * MaxWord1 / cmax;
	else
		saturation= 0;
		
	if (saturation == 0)
		hue= 0;
	else {      // determine hue
		int red_distance=   (cmax - rgb.red) * MaxWord1 / (cmax - cmin);
		int green_distance= (cmax - rgb.green) * MaxWord1 / (cmax - cmin);
		int blue_distance=  (cmax - rgb.blue) * MaxWord1 / (cmax - cmin);
		
		if (rgb.red == cmax)    // resulting color between yellow and magenta
			hue= blue_distance - green_distance;
		else if (rgb.green == cmax)  // resulting color between cyan and yellow
			hue= 2*MaxWord1 + red_distance - blue_distance;
		else                    // resulting color between magenta and cyan
			hue= 4*MaxWord1 + green_distance - red_distance;
		
		hue= (hue * 60) / MaxWord1;      // convert to degrees
		if (hue < 0)
			hue+= 360;              // make nonnegative
		else if (hue > 359)
			hue-= 360;              // make nonnegative
	}
	alpha= rgb.alpha;
}

HSVColor::HSVColor(const RGB &rgb)
{
	int cmax= Math::Max(Math::Max(rgb.red, rgb.green), rgb.blue);
	int cmin= Math::Min(Math::Min(rgb.red, rgb.green), rgb.blue);

	value= cmax;
	if (cmax)
		saturation= (cmax - cmin) * MaxWord1 / cmax;
	else
		saturation= 0;
		
	if (saturation == 0)
		hue= 0;
	else {      // determine hue
		int red_distance=   (cmax - rgb.red) * MaxWord1 / (cmax - cmin);
		int green_distance= (cmax - rgb.green) * MaxWord1 / (cmax - cmin);
		int blue_distance=  (cmax - rgb.blue) * MaxWord1 / (cmax - cmin);
		
		if (rgb.red == cmax)    // resulting color between yellow and magenta
			hue= blue_distance - green_distance;
		else if (rgb.green == cmax)  // resulting color between cyan and yellow
			hue= 2*MaxWord1 + red_distance - blue_distance;
		else                    // resulting color between magenta and cyan
			hue= 4*MaxWord1 + green_distance - red_distance;
		
		hue= (hue * 60) / MaxWord1;      // convert to degrees
		if (hue < 0)
			hue+= 360;              // make nonnegative
		else if (hue > 359)
			hue-= 360;              // make nonnegative
	}
	alpha= rgb.alpha;
}

OStream &HSVColor::PrintOn(OStream &os) const
{
	return os << hue SP << saturation SP << value SP << alpha NL;
}

IStream &HSVColor::ReadFrom(IStream &is)
{
	return is >> hue >> saturation >> value >> alpha;
}

//---- RGBColor ----------------------------------------------------------------

NewMetaImpl(RGBColor,Ink, (T(rgb), T(prec)));

RGBColor::RGBColor() : rgb((short)cMinRGB), Ink(-1)
{
	SetFlag(eInkChanged);
	prec= 0;
	port= 0;
}

RGBColor::RGBColor(RGBColor *cp) : Ink(-1)
{
	SetFlag(eInkChanged);
	if (cp) {
		rgb= cp->rgb;
		prec= cp->prec;
	}
	port= 0;
}

RGBColor::RGBColor(short r, short g, short b, short p) : rgb(r,g,b), Ink(-1)
{
	SetFlag(eInkChanged);
	prec= p;
	port= 0;
}

RGBColor::RGBColor(short graylevel, short p) : rgb(graylevel), Ink(-1)
{
	SetFlag(eInkChanged);
	prec= p;
	port= 0;
}

RGBColor::RGBColor(float graylevel, short p) : rgb(graylevel), Ink(-1)
{
	SetFlag(eInkChanged);
	prec= p;
	port= 0;
}

RGBColor::RGBColor(const RGB &c, short p) : rgb(c), Ink(-1)
{
	SetFlag(eInkChanged);
	prec= p;
	port= 0;
}

RGBColor::RGBColor(const HSVColor &hsv, short p) : rgb(hsv), Ink(-1)
{
	SetFlag(eInkChanged);
	prec= p;
	port= 0;
}

RGBColor::RGBColor(const RGBColor &rc) : rgb(rc.rgb), Ink(-1)
{
	SetFlag(eInkChanged);
	prec= 0;
	port= 0;
}

bool RGBColor::SetRGB(RGB *c, short p)
{
	bool rc= TRUE;

	rgb= *c;
	prec= Math::Range((short)0, (short)cMaxRGB, p);
	SetFlag(eInkChanged);
	if (prec == cMaxRGB && port) {
		WindowPort *wport= (WindowPort*)port;
		int newid= (int) wport->ColorMap()->AllocateAndSetCell(GetId(), c, wport);
		ResetFlag(eInkChanged);
		if (newid == GetId())
			rc= FALSE;
		SetId(newid);
	}
	//Changed();
	return rc;
}

bool RGBColor::SetHSV(short hue, short sat, short value, short p)
{
	HSVColor hc(hue, sat, value);
	RGB rc(hc);
	return SetRGB(&rc, p);
}

bool RGBColor::SetRGB(short r, short g, short b, short p)
{
	RGB rc(r, g, b);
	return SetRGB(&rc, p);
}

void RGBColor::SetInk(Port *p)
{
	port= p;
	p->SetColor(this);
}

OStream &RGBColor::PrintOn(OStream &s)
{
	Object::PrintOn(s);
	return s << rgb << prec SP;
}

IStream &RGBColor::ReadFrom(IStream &s)
{
	Object::ReadFrom(s);
	SetFlag(eInkChanged);
	SetId(-1);
	port= 0;
	s >> rgb >> prec;
	prec= 0;
	return s;
}

Object *RGBColor::ReadAndMap(IStream &is)
{
	return Object::ReadAndMap(is);
}

//---- RGBColorCell ------------------------------------------------------------

NewMetaImpl0(RGBColorCell,RGBColor);

RGBColorCell::RGBColorCell(int i)
{
	SetId(i);
	ResetFlag(eInkChanged);
}

void RGBColorCell::SetInk(Port *p)
{
	port= p;
	ResetFlag(eInkChanged);
	p->SetColor(this);
}

OStream& RGBColorCell::PrintOn(OStream &os)
{
	Object::PrintOn(os);
	return os << GetId() SP;
}

IStream& RGBColorCell::ReadFrom(IStream &is)
{
	int i;
	Object::ReadFrom(is);
	is >> i;
	SetId(i);
	return is;
}

Object *RGBColorCell::ReadAndMap(IStream &is)
{
	return Object::ReadAndMap(is);
}

//---- halftoning --------------------------------------------------------------

static void dither(int s, byte *m, int x, int y, int n);

static void dither4(int s, byte *m, int x, int y, int n, int d)
{
	if (n > 1) {
		register int i, j;
		dither(s, m, x, y, n);
		for (i= x; i < x+n; i++)
			for (j= y; j < y+n; j++)
				m[i*s+j]= m[i*s+j]*4+d;
	} else
		m[x*s+y]= d;
}

static void dither(int s, byte *m, int x, int y, int n)
{
	int n2= n/2;
	dither4(s, m, x,    y,    n2, 0);
	dither4(s, m, x+n2, y+n2, n2, 1);
	dither4(s, m, x+n2, y,    n2, 2);
	dither4(s, m, x,    y+n2, n2, 3);
}

Ink *Grey2Halftone(float f)
{
	int level;

	if (f < 0)
		f= 0.0;
	else if (f > 1.0)
		f= 1.0;
	f= 1.0 - f;

	level= (int)(f * levels + 0.5);
	if (level <= 0)
		return gInkWhite;
	if (level >= levels)
		return gInkBlack;
	if (patbms == 0) {
		patbms= new Bitmap* [levels];
		ditherm= new byte[levels];
		dither(cellsz, ditherm, 0, 0, cellsz);
	}
	if (patbms[level] == 0) {
		Bitmap *bm= patbms[level]= new Bitmap(cellsz, 1);

		for (int y= 0; y < cellsz; y++) {
			for (int x= 0; x < cellsz; x++)
				if (level > ditherm[x*cellsz+y])
					bm->SetPixel(x, y, 1);
				else
					bm->SetPixel(x, y, 0);
		}
	}
	return patbms[level];
}

